//
//  ZCScraping.m
//  PicUploader
//
//  Created by Chen Zhang on 8/16/08.
//  Copyright 2008 overboming. All rights reserved.
//  A useful class to fetch web pages, post data
//  to certain URL or uploading a file 

#import "ZCScraping.h"
#import "AppController.h"
#import "PUPicture.h"
#import "AllBoardNameFormatter.h"

#define LOGIN_SERVER @"http://bbs.fudan.sh.cn/bbs/login"
#define LOGOUT_SERVER @"http://bbs.fudan.sh.cn/bbs/logout"
#define POST_SERVER @"http://bbs.fudan.sh.cn/bbs/snd?bid="
#define FILE_SERVER @"http://bbs.fudan.sh.cn/bbs/upload"
#define BOUNDARY @"---------------------------168072824752491622650073"
#define FORM_FLE_INPUT @"upform"
#define MAIL_SERVER @"http://bbs.fudan.sh.cn/bbs/sndmail?userid="

@implementation ZCScraping 


- (id) init
{
    [super init];
     userid = [[NSString alloc] initWithString:@""];
     password = [[NSString alloc] initWithString:@""];
    return self;
}

- (id) initWithUserName:(NSString *)username Password:(NSString *)passcode Controller:(AppController *)ctrl
{
    [super init];
    userid = [username retain];
    password = [passcode retain];
    controler = [ctrl retain];
    return self;
}

- (void)Login
{
    //compose post data and url
    //NSLog(@"try to login");
    NSString *post = [[NSString alloc] initWithFormat:
                      @"%@=%@&%@=%@",@"id",userid,
                      @"pw", password];
    NSString *returnHTML = [self PostToWebPage:LOGIN_SERVER withData:post];
	NSLog(@"boliwa attention starts:");
	NSLog(@"%@",returnHTML);
	NSLog(@"boliwa attention ends:");
    NSString *errorMessage = [self loginValidation:returnHTML];
    if(errorMessage != nil){
         NSAlert *alert = [NSAlert alertWithMessageText:@"Login Error! "
                              defaultButton:@"OK"
                              alternateButton:nil
                              otherButton:nil
                              informativeTextWithFormat:@"Error is listed as follows\n %@", errorMessage];
        [alert runModal];
        [[controler progress] stopAnimation:self];
        [[controler statusBar]  setStringValue:@"Not Logged In"];
        [controler endCurrentTask];

    }
    //correct    
	//NEW disabled for 
    //[self parseCookieFromHTML:returnHTML];
    [[controler statusBar]  setStringValue:@"Logged in, We are ready"];
}



- (NSString *)Logout
{
    NSString *url = LOGOUT_SERVER;  
   return [self FetchWebPage:url];
}


- (NSURLRequest *)postRequestWithURL: (NSURL *)url        
                             boundry: (NSString *)boundry 
                                data: (NSData *)data      
                            filename:(NSString *)filename
                            boardname:(NSString *)boardname
{
    NSMutableURLRequest *urlRequest =
    [NSMutableURLRequest requestWithURL:url];
    [urlRequest setHTTPMethod:@"POST"];
    
    
    [urlRequest setValue:@"bbs.fudan.sh.cn" forHTTPHeaderField:@"Host"];
    [urlRequest setValue:@"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.1) Gecko/2008070206 Firefox/3.0.1" forHTTPHeaderField:@"User-Agent"];
    [urlRequest setValue:@"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" forHTTPHeaderField:@"Accept"];
    [urlRequest setValue:@"300" forHTTPHeaderField:@"Keep-Alive"];
    [urlRequest setValue:@"keep-alive" forHTTPHeaderField:@"Connection"];
    [urlRequest setValue:[[NSString alloc] initWithFormat:@"http://bbs.fudan.sh.cn/cgi-bin/bbs/bbspreupload?board=%@",boardname] forHTTPHeaderField:@"Referer"];
	//[urlRequest setValue:cookiestring forHTTPHeaderField:@"Cookie"];
	[urlRequest setHTTPShouldHandleCookies:YES];

    [urlRequest setValue:
     [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundry]
      forHTTPHeaderField:@"Content-Type"];
    
    NSMutableData *postData =
    [NSMutableData dataWithCapacity:[data length] + 512];  //padding
    
    [urlRequest setValue:
        [NSString stringWithFormat:@"%@",[postData length]] 
      forHTTPHeaderField:@"Content-Length"];
    
    [postData appendData:
     [[NSString stringWithFormat:@"--%@\r\n", boundry] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [postData appendData:
     [[NSString stringWithFormat:
       @"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\nContent-Type: %@\r\n\r\n", FORM_FLE_INPUT,filename,[self getContentTypeFromFileName:filename]]
      dataUsingEncoding:NSUTF8StringEncoding]];
    
    [postData appendData:data]; //real pic data
    [postData appendData:
     [[NSString stringWithFormat:@"\r\n--%@\r\n", boundry] dataUsingEncoding:NSUTF8StringEncoding]];
    
    [postData appendData:
            [[[NSString alloc] initWithString:@"Content-Content-Disposition: form-data; name=\"board\";\r\n\r\n"]
       dataUsingEncoding:NSUTF8StringEncoding]];
    [postData appendData:
     [[NSString stringWithFormat:@"%@",boardname] 
       dataUsingEncoding:NSUTF8StringEncoding]];
    [postData appendData:
     [[NSString stringWithFormat:@"\r\n--%@--",boundry]
       dataUsingEncoding:NSUTF8StringEncoding]];
        
    
    [urlRequest setHTTPBody:postData];
    return urlRequest;
}

- (NSString *)getFileNameFromPath: (NSString *) path
{
    NSRange seperatorRange;
    seperatorRange = [path rangeOfString:@"/"
                        options:(NSBackwardsSearch)];
    NSRange filenameRange;
    filenameRange.location = seperatorRange.location + 1;
    filenameRange.length = [path length] - seperatorRange.location - 1;
    NSString *filename = [path substringWithRange:filenameRange];
    return filename;
}

- (NSString *)getContentTypeFromFileName:(NSString *)filename
{
    if([filename hasSuffix:@".png"] || [filename hasSuffix:@".PNG"]){
        return @"image/png";
    }
    if([filename hasSuffix:@".GIF"] || [filename hasSuffix:@".gif"]){
        return @"image/gif";
    }
    if([filename hasSuffix:@".pdf"] || [filename hasSuffix:@".PDF"]){
        return @"file/pdf";
    }
    if([filename hasSuffix:@".jpg"] || [filename hasSuffix:@".jpeg"]){
        return @"image/jpg";
    }
    return @"image/jpg";
}

//!!! serious memory leak 
- (void) UploadFileWithPath:(NSString *)path toURL:(NSString *)uurl withBoardName:(NSString *)board
{
    //I take the assumption that during uploading, we
    //are always on line 
    
    NSData *fileData = [[NSData alloc] initWithContentsOfFile:path];
    //NSString *postLength = [NSString stringWithFormat:@"%d", [fileData length]];
    //for file transfer, we want call back
    if (!fileData || [fileData length] == 0) {
        [self uploadSucceeded:NO];
        return;
    }
    NSURLRequest *urlRequest = [self postRequestWithURL:[[NSURL alloc] initWithString:uurl] boundry:BOUNDARY data:fileData filename:[self getFileNameFromPath:path] boardname:board];
    
    if (!urlRequest) {
        [self uploadSucceeded:NO];
        return;
    }    
    NSURLConnection * connection =
    [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
    if (!connection) {
        [self uploadSucceeded:NO];
        [fileData release];
    }           
}
- (void) UploadThreadBranch:(id)path
{
    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
    NSString *boardName = [[controler boardNameBox] stringValue];
    [self UploadFileThreadWithPath:path toURL:FILE_SERVER withBoardName:boardName];
    //will exit automatically
    [loopPool release];
}

//upload file with no delegate, blocking io suitable for a thread;
- (void) UploadFileThreadWithPath:(NSString *)path toURL:(NSString *)uurl withBoardName:(NSString *)board
{
    NSData *fileData = [[NSData alloc] initWithContentsOfFile:path];
    //NSString *postLength = [NSString stringWithFormat:@"%d", [fileData length]];
    //for file transfer, we want call back
    if (!fileData || [fileData length] == 0) {
        [self uploadSucceeded:NO];
        return;
    }
    NSURLRequest *urlRequest = [self postRequestWithURL:[[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@?b=%@",uurl,board]] boundry:BOUNDARY data:fileData filename:[self getFileNameFromPath:path] boardname:board];
    
    if (!urlRequest) {
        [self uploadSucceeded:NO];
        return;
    }    
    
    NSData *picInfoData;
    NSHTTPURLResponse *response;
    NSError *error;
    picInfoData = [NSURLConnection sendSynchronousRequest:urlRequest
                                    returningResponse:&response
                                                error:&error];
    //NSLog(@"dicts %@",[response allHeaderFields]);
    
    if (!picInfoData) {
        //enabe retry, ask user?
       // NSAlert *alert = [NSAlert alertWithError:error];
        PUPicture *donePic = [[controler findPictureByFilePath:path] retain];
        [donePic setPicStatus:[[NSMutableAttributedString alloc]
                               initWithString:@"Uploading"]];  //set to uploading then re-upload
        //NSLog(@"Time out, auto reconnecting...");
        //[alert runModal];
        [self UploadFileThreadWithPath:path toURL:uurl withBoardName:board];
//        [donePic release];
//        
//        NSAlert *alert = [NSAlert alertWithMessageText:@"Upload Error"
//                                         defaultButton:@"Retry"
//                                       alternateButton:@"Cancel"
//                                           otherButton:nil
//                             informativeTextWithFormat:@"Upload Error for pic\n %@",path];
//        //NSLog(@"Starting alert sheet");
//        [alert beginSheetModalForWindow:[[controler progress] window]
//                          modalDelegate:self
//                         didEndSelector:@selector(retrySheet:code:context:)
//                            contextInfo:[NSArray arrayWithObjects:path,uurl,board,nil]];
        return;
    }
    NSLog(@"%@\n",[self ConvertDataToString:picInfoData]);
	NSString *name = [self getStoredFileName:[self ConvertDataToString:picInfoData]];
	if (name != nil) {
		[controler uploadCompleteForOneThread:name Path:path];
	}
    
    [fileData release]; //release
}


- (void)retrySheet:(NSWindow *)sheet
       code:(int)returnCode
      context:(void *)contextInfo{
    if (returnCode == NSAlertDefaultReturn) {
    NSMutableArray *array = [[NSMutableArray alloc] initWithArray:contextInfo copyItems:NO];
    [self UploadFileThreadWithPath:[array objectAtIndex:0] toURL:[array objectAtIndex:1] withBoardName:[array objectAtIndex:2]]; //retry
    }
    else if(returnCode == NSAlertAlternateReturn){
        //NSLog(@"don't do this pic");
    }
}



- (void) uploadSucceeded:(BOOL) bo
{
    if(bo){
        ;
    }
    else{
        [controler endCurrentTask];
    }
}


- (NSString *)PostArticle:(NSString *)boardname withData:(NSString *)postString
{
	AllBoardNameFormatter *formatter = [[AllBoardNameFormatter alloc] init];
    return [self PostToWebPage:[NSString stringWithFormat:@"%@%@",POST_SERVER,[[formatter boardNameDic] objectForKey:boardname]] withData:postString];
}

- (NSString *)PostSuggestion:(NSString *)postString
{
    return [self PostToWebPage:[NSString stringWithFormat:@"%@",MAIL_SERVER] withData:postString];
}


- (NSString *)PostToWebPage:(NSString *)uurl withData:(NSString *)post
{
    NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
    NSData *postData = [post dataUsingEncoding:enc allowLossyConversion:YES];
    
    NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
    
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
	[request setHTTPShouldHandleCookies:YES];
    [request setURL:[NSURL URLWithString:uurl]];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"keep-live" forHTTPHeaderField:@"Connection"];
    [request setValue:@"http://bbs.fudan.sh.cn" forHTTPHeaderField:@"Referer"];
    [request setValue:@"300" forHTTPHeaderField:@"Keep-Alive"];

    [request setHTTPBody:postData];
    [request setTimeoutInterval:10.0];
    
    NSData *urlData;
    NSHTTPURLResponse *response;
    NSError *error;
    urlData = [NSURLConnection sendSynchronousRequest:request
                                    returningResponse:&response
                                                error:&error];
	
	NSLog(@"response dictionary %@",[response allHeaderFields]);
    if (!urlData) {
        [controler endCurrentTask];
        return nil;
    }
	
	NSLog(@"%@\n",[self ConvertDataToString:urlData]);
    return [self ConvertDataToString:urlData];
}

//with cookie
- (NSString *)FetchWebPage:(NSString *)uurl
{
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
	[request setHTTPShouldHandleCookies:YES];
    [request setURL:[NSURL URLWithString:uurl]];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"keep-live" forHTTPHeaderField:@"Connection"];
    [request setValue:@"300" forHTTPHeaderField:@"Keep-Alive"];
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
    
    NSData *urlData;
    NSHTTPURLResponse *response;
    NSError *error;
    urlData = [NSURLConnection sendSynchronousRequest:request
                                    returningResponse:&response
                                                error:&error];
    
        
    if (!urlData) {
        
        NSString *errorMessage = [error localizedDescription];
        if(errorMessage != nil){
            NSAlert *alert = [NSAlert alertWithMessageText:@"Connection Error! "
                                             defaultButton:@"OK"
                                           alternateButton:nil
                                               otherButton:nil
                                 informativeTextWithFormat:@"Error is listed as follows\n %@", errorMessage];
            [alert runModal];            
        }
        
        [controler endCurrentTask];
        return nil;
    }
    return [self ConvertDataToString:urlData];
}



- (NSString *)ConvertDataToString:(NSData *)data
{
    //we handle GB18030 in this project according to server settings
    NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
    return [[NSString alloc] initWithData:data encoding:enc];
}

- (NSString *)parseCookieFromHTML:(NSString *)html
{    
    NSString *firstpart = [html substringWithRange:[self getCookieContentFromString:html]];    
    NSRange secondRange;
    NSRange firstpartRange = [self getCookieContentFromString:html];
    secondRange.location = firstpartRange.location + firstpartRange.length + 5;
    secondRange.length = [html length] - secondRange.location - 1;
    html = [html substringWithRange:secondRange];    
    NSString *secondpart = [html substringWithRange:[self getCookieContentFromString:html]];
    NSRange thirdRange;
    NSRange secondpartRange = [self getCookieContentFromString:html];
    thirdRange.location = secondpartRange.location + secondpartRange.length + 5;
    thirdRange.length = [html length] - thirdRange.location - 1;
    html = [html substringWithRange:thirdRange];
    NSString *thirdpart = [html substringWithRange:[self getCookieContentFromString:html]];
    cookiestring = [[NSString alloc] initWithFormat:@"%@; %@; %@",firstpart,secondpart,thirdpart];
   // //NSLog(cookiestring);
    return cookiestring;
}

- (NSRange)getCookieContentFromString:(NSString *) subst
{
    NSRange startl = [subst rangeOfString:@"<script>"];
    NSRange endl = [subst rangeOfString:@"</script>"];
    NSRange cookierange;
    cookierange.location = startl.location + 25;
    cookierange.length = (endl.location - startl.location) - 26;
    return cookierange;
}

- (NSString *)getStoredFileName:(NSString *)result
{
    NSString *startPattern = @"</user><url>";
    NSString *endPattern = @"</url></bbsupload>";
	
    NSRange startRange = [result rangeOfString:startPattern];
    NSRange endRange = [result rangeOfString:endPattern];    
    NSRange fileNamePattern;
	
    fileNamePattern.location = startRange.location + [startPattern length];
    fileNamePattern.length = endRange.location - startRange.location - [startPattern length];
	if (startRange.location < 0 || endRange.location < 0) {
		return nil;
	}
    NSString *filename = [[NSString alloc] initWithString:[result substringWithRange:fileNamePattern]];
    [result release];  //not useful wahaha
    return filename;
}
- (BOOL) detectLoggedIn
{
    //check whether my cookie is valid
    NSString *gotFeedBack = [self FetchWebPage:@"http://bbs.fudan.sh.cn/bbs/sec"];
    //ugly hack, but effective, could be better
    if([gotFeedBack rangeOfString:@";;"].location != NSNotFound 
                    || [gotFeedBack rangeOfString:@"<bbssec s='    ;;'>"].location != NSNotFound
        ){  //not logged in
        return NO;
    }
    return YES;
}

- (NSString *) loginValidation:(NSString *)html
{
	NSLog(@"%@\n",userid);
	
    NSString *errorMessage = nil;
    if( [html length] <= 20 && [html rangeOfString:@"Oops"].location >= 0){
        //page not found or something else
        errorMessage = @"Oops! Maybe BBS is down again...";
    }
    else if([html rangeOfString:@"您不能登录更多帐号了"].location != NSNotFound){
        errorMessage = @"您不能登录更多帐号了";
    }
	
	else if([html rangeOfString:userid options:NSCaseInsensitiveSearch].location == NSNotFound){
		errorMessage = @"Not Logged in!";
	}
    return errorMessage;
}



@synthesize userid;
@synthesize password;
@synthesize cookiestring;
@end
